package de.mxro.process.internal;

import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.lang.ProcessBuilder.Redirect;
import java.util.Date;
import java.util.concurrent.atomic.AtomicLong;

import de.mxro.process.ProcessListener;
import de.mxro.process.XProcess;

public class Engine {

    public static XProcess startProcess(final String[] command,
            final ProcessListener listener, final File folder) {

        final ProcessBuilder pb;
        final Process process;

        try {

            pb = new ProcessBuilder(command);
            pb.directory(folder);
            pb.redirectOutput(Redirect.PIPE);
            pb.redirectInput(Redirect.PIPE);
            process = pb.start();
        } catch (final IOException e) {
            throw new RuntimeException(e);
        }

        final AtomicLong lastOutput = new AtomicLong();
        final long startTime = new Date().getTime();
        lastOutput.set(0);



        final OutputStream outputStream = process.getOutputStream();
        final InputStream inputStream = process.getInputStream();
        final InputStream errorStream = process.getErrorStream();


        //BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));


//      String line;
//      try {
//          line = reader.readLine();
//          while (line != null && !line.trim().equals("--EOF--")) {
//               listener.onOutputLine(line);
//               line = reader.readLine();
//           }
//      } catch (IOException e1) {
//          // TODO Auto-generated catch block
//          e1.printStackTrace();
//      }


        final StreamReader streamReader = new StreamReader(inputStream,
                new StreamListener() {

                    @Override
                    public void onOutputLine(final String line) {
                        //System.out.println("Receiving line");
                        lastOutput.set(new Date().getTime());
                        listener.onOutputLine(line);
                    }

                    @Override
                    public void onError(final Throwable t) {
                        listener.onError(new Exception(
                                "Error while reading standard output", t));
                    }

                    @Override
                    public void onClosed() {

                    }
                });



        final StreamReader errorStreamReader = new StreamReader(errorStream,
                new StreamListener() {

                    @Override
                    public void onOutputLine(final String line) {
                        lastOutput.set(new Date().getTime());
                        listener.onErrorLine(line);
                    }

                    @Override
                    public void onError(final Throwable t) {
                        listener.onError(new Exception(
                                "Error while reading error output", t));
                    }

                    @Override
                    public void onClosed() {

                    }
                });

        new Thread() {

            @Override
            public void run() {
                errorStreamReader.read();
            }

        }.start();

        new Thread() {

            @Override
            public void run() {
                try {
                    streamReader.read();

                    final int returnValue = process.waitFor();

                    //while (lastOutput.get() == 0 && new Date().getTime() - startTime < 500) {
                    //  Thread.sleep(10);
                    //}

                    //while ( new Date().getTime() - lastOutput.get() < 1000) {
                    //  System.out.println("DELAY ... "+(new Date().getTime() - lastOutput.get()));
                    //  Thread.sleep(500);
                    //}
                    //System.out.println("processes finished with "+returnValue);

                    listener.onProcessQuit(returnValue);
                } catch (final InterruptedException e) {
                    listener.onError(e);
                    return;
                }

            }

        }.start();

        return new XProcess() {

            @Override
            public synchronized void sendLine(final String line) {
                final BufferedWriter writer = new BufferedWriter(
                        new OutputStreamWriter(outputStream));
                try {
                    writer.append(line);
                } catch (final IOException e) {
                    listener.onError(e);
                }
            }

            @Override			
			public void sendByte(byte[] b, int off, int length) {				
                try {
					outputStream.write(b, off, length);
					//outputStream.flush();
                } catch (final IOException e) {
                    listener.onError(e);
                }				
			}							

            @Override
            public void destory() {
				
                process.destroy();	
				
                try {
                    process.waitFor();
                } catch (final InterruptedException e) {
                    throw new RuntimeException(e);
                }
				
                try {
                    errorStreamReader.stop();

                    outputStream.close();
                    inputStream.close();
                    errorStream.close();

                } catch (final IOException e) {
                    listener.onError(e);
                }
			
                //process.destroyForcibly();
            }
        };

    }

}
